using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Core;

namespace WebViewGenerator
{
	public class ValidatorFileGenerator
	{
		private const string ERROR_DATE_MESSAGE = "{FIELD_NAME} must be in the form a date, i.e. 12/21/2009.";
		private const string ERROR_DEFINITION = @"	<Error id=""{ERROR_ID}"" type=""validation"" formField=""{FIELD_NAME}"">{MESSAGE}</Error>";
		private const string ERROR_DICTIONARY_DIRECTORY = @"GeneratedFiles\Web\Content\v1\us\";
		private const string ERROR_FLOAT_MESSAGE = "{FIELD_NAME} must be in a form of a decimal number.";
		private const string ERROR_INT_MESSAGE = "{FIELD_NAME} must be in a form of an integer.";

		private const string REGEXP_RULE =
			@"			<regex field=""{PROPERTY_NAME_LOWER}"" ErrorId=""{ERROR_ID}"">
				<properties>
					<pattern>{REGEXP}</pattern>
				</properties>
			</regex>";

		private const string REQUIRED_RULE =
			@"		<and>
{REGEXP_RULE}
			<regex field=""{PROPERTY_NAME_LOWER}"" ErrorId=""100"">
				<properties>
					<pattern>\S</pattern>
				</properties>
			</regex>
		</and>";


		private const string VALIDATOR_DIRECTORY = @"GeneratedFiles\Web\Validators\";
		private Type classType;
		private string currentClass = "";
		private string errorDictionaryFile;
		private int errorIdCount = 0;
		private int prependStateNumber;
		private string validatorFile;

		public ValidatorFileGenerator() {}


		public ValidatorFileGenerator(Assembly assembly)
		{
			this.Assembly = assembly;
		}


		public Assembly Assembly { get; set; }


		public void Execute(IEnumerable<string> selectedClassNames,
		                    int prependStateNumberStart)
		{
			//read in the template
			TextReader reader = new StreamReader(AppDomain.CurrentDomain.BaseDirectory + @"\TemplateFiles\ValidatorFile.template");

			string template = reader.ReadToEnd();

			TextWriter writer;

			this.prependStateNumber = prependStateNumberStart;
			this.errorDictionaryFile =
				@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<ErrorList>";

			foreach (string fullName in selectedClassNames) {
				//create a new template string
				this.validatorFile = template;

				this.classType = this.Assembly.GetType(fullName);
				string className = classType.Name;

				this.errorDictionaryFile += Environment.NewLine + "<!-- " + className + " Messages -->" + Environment.NewLine;

				this.currentClass = className;

				this.validatorFile = this.validatorFile.Replace("{CLASS_NAME}", className);
				this.validatorFile = this.validatorFile.Replace("{CLASS_NAME_UPPER}", className.ToUpper());
				this.validatorFile = this.validatorFile.Replace("{CLASS_NAME_LOWER}", className.ToLower());

				string rules = this.AddRules();

				if (!string.IsNullOrEmpty(rules)) {
					this.validatorFile = this.validatorFile.Replace("{EXPRESSIONS}", rules);

					Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + VALIDATOR_DIRECTORY);

					writer = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + VALIDATOR_DIRECTORY + className + ".validators.config");

					writer.Write(this.validatorFile);

					writer.Flush();
					writer.Close();
				}

				this.prependStateNumber++;
				this.errorIdCount = 0;
			}

			this.errorDictionaryFile += "</ErrorList>";

			Directory.CreateDirectory(AppDomain.CurrentDomain.BaseDirectory + ERROR_DICTIONARY_DIRECTORY);

			writer = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + ERROR_DICTIONARY_DIRECTORY + "ErrorDictionary." + prependStateNumberStart + ".xml");

			writer.Write(this.errorDictionaryFile);

			writer.Flush();
			writer.Close();
		}


		private string AddRules()
		{
			string rules = "";
			//foreach property generate a rule
			//skip id and version

			foreach (MemberInfo member in this.classType.GetMembers()) {
				if (member.Name != "Id" && member.Name != "Version") {
					string rule = this.GetPropertyRule(member);
					if (!string.IsNullOrEmpty(rule)) {
						rules += rule + Environment.NewLine;
					}
				}
			}

			return rules;
		}


		private string GetPropertyRule(MemberInfo member)
		{
			string singleRule = "";

			//This is for the cases where the property type is an object and will not need a rule.
			bool addRule = true;

			if (member.MemberType == MemberTypes.Property) {
				Type t = Utilities.GetRealTypeFromMemberProperty(member);

				string fieldName = this.currentClass.ToLower() + "_" + member.Name.ToLower();

				string rule;

				singleRule = REQUIRED_RULE.Replace("{PROPERTY_NAME_LOWER}", fieldName);

				if (t == typeof (string)) {
					rule = "";
				} else {
					rule = REGEXP_RULE.Replace("{PROPERTY_NAME_LOWER}", fieldName);
                    
					string currentErrorId = this.prependStateNumber + this.errorIdCount.ToString().PadLeft(3, '0');

					string message = "";

					rule = rule.Replace("{ERROR_ID}", currentErrorId);

					if (t == typeof (int) || t == typeof (long)) {
						rule = rule.Replace("{REGEXP}", @"\d+");
						message = ERROR_INT_MESSAGE.Replace("{FIELD_NAME}", member.Name);
					} else if (t == typeof (DateTime)) {
						rule = rule.Replace("{REGEXP}", @"(\d+)/(\d+)/(\d+)");
						message = ERROR_DATE_MESSAGE.Replace("{FIELD_NAME}", member.Name);
					} else if (t == typeof (double) || t == typeof (float)) {
						rule = rule.Replace("{REGEXP}", @"\d*[0-9](|.\d*[0-9]|,\d*[0-9])?");
						message = ERROR_FLOAT_MESSAGE.Replace("{FIELD_NAME}", member.Name);
					} else {
						addRule = false;
					}

					if (!string.IsNullOrEmpty(message)) {
						this.errorDictionaryFile += ERROR_DEFINITION.Replace("{MESSAGE}", message).Replace("{ERROR_ID}", currentErrorId).Replace("{FIELD_NAME}", fieldName) + Environment.NewLine;
					}
				}

				if (addRule) {
					singleRule = singleRule.Replace("{REGEXP_RULE}", rule);
					this.errorIdCount++;
				} else {
					singleRule = "";
				}
			}

			return singleRule;
		}
	}
}